package com.muyu.payment.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.muyu.common.core.exception.base.BaseException;
import com.muyu.common.core.web.page.PageQueryModel;
import com.muyu.payment.domain.PaymentRefund;
import com.muyu.payment.domain.PaymentOrder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.muyu.payment.mapper.PaymentRefundMapper;
import com.muyu.payment.domain.model.PaymentRefundQueryModel;
import com.muyu.payment.domain.model.PaymentRefundPageQueryModel;
import com.muyu.payment.domain.model.PaymentRefundSaveModel;
import com.muyu.payment.domain.model.PaymentRefundAuditModel;
import com.muyu.payment.service.PaymentRefundService;
import com.alipay.api.AlipayApiException;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.muyu.payment.config.AliPayConfig;
import com.muyu.payment.service.PaymentOrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import com.muyu.common.security.utils.SecurityUtils;

/**
 * 支付退款申请Service业务层处理
 *
 * @author muyu
 * @date 2025-09-16
 */
@Slf4j
@Service
public class PaymentRefundServiceImpl extends ServiceImpl<PaymentRefundMapper, PaymentRefund> implements PaymentRefundService {

    @Autowired
    private PaymentOrderService paymentOrderService;

    @Autowired
    private AliPayConfig aliPayConfig;

    private static final String GATEWAY_URL = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";

    /**
     * 查询支付退款申请列表
     *
     * @param pageQueryModel 支付退款申请
     * @return 支付退款申请
     */
    @Override
    public PageQueryModel<PaymentRefundPageQueryModel> pageQuery(PaymentRefundQueryModel pageQueryModel) {
        LambdaQueryWrapper<PaymentRefund> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getRefundNo()), PaymentRefund::getRefundNo, pageQueryModel.getRefundNo());
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getOrderNo()), PaymentRefund::getOrderNo, pageQueryModel.getOrderNo());
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getUserId()), PaymentRefund::getUserId, pageQueryModel.getUserId());
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getRefundStatus()), PaymentRefund::getRefundStatus, pageQueryModel.getRefundStatus());
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getAuditor()), PaymentRefund::getAuditor, pageQueryModel.getAuditor());
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getRefundChannel()), PaymentRefund::getRefundChannel, pageQueryModel.getRefundChannel());
        queryWrapper.orderByDesc(PaymentRefund::getCreateTime);
        Page<PaymentRefund> page = this.page(pageQueryModel.buildPage(), queryWrapper);
        return PageQueryModel.of(page).cover(PaymentRefundPageQueryModel::infoBuild);
    }

    /**
     * 查询支付退款申请列表
     * @param pageQueryModel 支付退款申请
     * @return 支付退款申请
     */
    @Override
    public List<PaymentRefund> exportList(PaymentRefund pageQueryModel) {
        LambdaQueryWrapper<PaymentRefund> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getRefundNo()), PaymentRefund::getRefundNo, pageQueryModel.getRefundNo());
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getOrderNo()), PaymentRefund::getOrderNo, pageQueryModel.getOrderNo());
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getUserId()), PaymentRefund::getUserId, pageQueryModel.getUserId());
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getRefundStatus()), PaymentRefund::getRefundStatus, pageQueryModel.getRefundStatus());
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getAuditor()), PaymentRefund::getAuditor, pageQueryModel.getAuditor());
        queryWrapper.eq(ObjectUtil.isNotNull(pageQueryModel.getRefundChannel()), PaymentRefund::getRefundChannel, pageQueryModel.getRefundChannel());
        queryWrapper.orderByDesc(PaymentRefund::getCreateTime);
        return this.list(queryWrapper);
    }

    /**
     * 支付退款申请添加
     *
     * @param saveModel 支付退款申请
     */
    @Override
    public void save(PaymentRefundSaveModel saveModel) {
        PaymentRefund paymentRefund = PaymentRefund.builder()
                .refundNo(IdUtil.getSnowflakeNextIdStr())
                .orderNo(saveModel.getOrderNo())
                .userId(saveModel.getUserId())
                .refundAmount(saveModel.getRefundAmount())
                .refundReason(saveModel.getRefundReason())
                .refundStatus(1) // 待审核
                .remark(saveModel.getRemark())
                .createTime(new Date())
                .build();
        this.save(paymentRefund);
    }

    /**
     * 申请退款
     *
     * @param saveModel 退款申请信息
     * @return 退款申请号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String applyRefund(PaymentRefundSaveModel saveModel) {
        // 检查订单是否可以申请退款
        if (!canApplyRefund(saveModel.getOrderNo(), saveModel.getUserId())  ) {
            throw new RuntimeException("订单不满足退款条件");
        }

        // 检查是否已有待审核的退款申请
        LambdaQueryWrapper<PaymentRefund> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PaymentRefund::getOrderNo, saveModel.getOrderNo())
                .eq(PaymentRefund::getUserId, saveModel.getUserId())
                .in(PaymentRefund::getRefundStatus, 1, 4); // 待审核或退款中
        long count = this.count(queryWrapper);
        if (count > 0) {
            throw new RuntimeException("该订单已有待处理的退款申请");
        }

        // 获取原订单信息
        PaymentOrder order = paymentOrderService.getByOrderNo(saveModel.getOrderNo());
        if (order == null) {
            throw new RuntimeException("原订单不存在");
        }

        // 验证退款金额不能超过订单金额
        if (saveModel.getRefundAmount().compareTo(order.getAmount()) > 0) {
            throw new RuntimeException("退款金额不能超过订单金额");
        }

        // 将原订单状态置为"申请退款中"，以订单为主进行流转
        // 这里约定：5 表示"申请退款中"（如有不同，请按实际字典值替换）
        order.setOrderStatus(5L);
        order.setUpdateBy(SecurityUtils.getUsername()); // 设置更新者为当前登录人
        order.setUpdateTime(new Date());
        paymentOrderService.updateById(order);

        // 创建退款申请
        String refundNo = IdUtil.getSnowflakeNextIdStr();
        PaymentRefund paymentRefund = PaymentRefund.builder()
                .refundNo(refundNo)
                .orderNo(saveModel.getOrderNo())
                .userId(saveModel.getUserId())
                .refundAmount(saveModel.getRefundAmount())
                .refundReason(saveModel.getRefundReason())
                .refundStatus(1) // 待审核
                .refundChannel(order.getPayChannel()) // 使用原支付渠道
                .remark(saveModel.getRemark())
                .createTime(new Date())
                .build();

        this.save(paymentRefund);
        log.info("用户申请退款成功：订单号={}，退款申请号={}，退款金额={}", 
                saveModel.getOrderNo(), refundNo, saveModel.getRefundAmount());
        
        return refundNo;
    }

    /**
     * 审核退款申请
     *
     * @param auditModel 审核信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditRefund(PaymentRefundAuditModel auditModel) {
        PaymentRefund paymentRefund = this.getById(auditModel.getId());
        if (paymentRefund == null) {
            throw new RuntimeException("退款申请不存在");
        }

        if (paymentRefund.getRefundStatus() != 1) {
            throw new RuntimeException("只能审核待审核状态的退款申请");
        }

        // 更新审核信息（当前登录人+当前时间）
        String auditor = auditModel.getAuditor();
        if (auditor == null || auditor.isEmpty()) {
            auditor = SecurityUtils.getUsername();
        }
        
        // 审核结果映射：1:通过->2, 2:拒绝->3
        Integer refundStatus = auditModel.getAuditResult() == 1 ? 2 : 3;
        paymentRefund.setRefundStatus(refundStatus);
        paymentRefund.setAuditor(auditor);
        paymentRefund.setAuditTime(new Date());
        paymentRefund.setAuditOpinion(auditModel.getAuditOpinion());
        paymentRefund.setUpdateTime(new Date());

        this.updateById(paymentRefund);

        // 如果审核通过（1），可自动触发退款；若审核拒绝（2），回滚订单状态为"已支付(2)"
        if (auditModel.getAuditResult() == 1) {
            // 审核通过：保持订单状态为"申请退款中(5)"，然后执行退款
            try {
                processRefund(paymentRefund.getRefundNo());
            } catch (Exception e) {
                log.error("自动执行退款失败：{}", e.getMessage());
                // 这里可以选择是否回滚审核结果
            }
        } else if (auditModel.getAuditResult() == 2) {
            // 审核拒绝：订单状态设为"退款失败(7)"
            PaymentOrder order = paymentOrderService.getByOrderNo(paymentRefund.getOrderNo());
            if (order != null) {
                order.setOrderStatus(7L); // 退款失败
                order.setUpdateBy(SecurityUtils.getUsername()); // 设置更新者为当前登录人
                order.setUpdateTime(new Date());
                paymentOrderService.updateById(order);
                log.info("审核拒绝，订单状态已更新为退款失败：订单号={}", paymentRefund.getOrderNo());
            }
        }

        log.info("退款申请审核完成：退款申请号={}，审核结果={}，审核人={}", 
                paymentRefund.getRefundNo(), auditModel.getAuditResult(), auditModel.getAuditor());
    }

    /**
     * 执行退款
     *
     * @param refundNo 退款申请号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processRefund(String refundNo) {
        PaymentRefund paymentRefund = this.getOne(
                new LambdaQueryWrapper<PaymentRefund>().eq(PaymentRefund::getRefundNo, refundNo)
        );

        if (paymentRefund == null) {
            throw new RuntimeException("退款申请不存在");
        }

        if (paymentRefund.getRefundStatus() != 2) {
            throw new RuntimeException("只有审核通过的退款申请才能执行退款");
        }

        try {
            // 更新状态为退款中
            paymentRefund.setRefundStatus(4);
            paymentRefund.setUpdateTime(new Date());
            this.updateById(paymentRefund);

            // 调用第三方支付接口进行实际退款
            boolean refundSuccess = channelRefund(paymentRefund);

            if (refundSuccess) {
                // 退款成功
                paymentRefund.setRefundStatus(5);
                paymentRefund.setRefundTime(new Date());
                paymentRefund.setThirdRefundNo("TP_" + IdUtil.getSnowflakeNextIdStr());
                
                // 更新原订单状态为已退款
                PaymentOrder order = paymentOrderService.getByOrderNo(paymentRefund.getOrderNo());
                if (order != null) {
                    order.setOrderStatus(9L); // 已退款（按现有字典定义）
                    order.setUpdateBy(SecurityUtils.getUsername()); // 设置更新者为当前登录人
                    order.setUpdateTime(new Date());
                    paymentOrderService.updateById(order);
                    log.info("退款成功，订单状态已更新为已退款：订单号={}", paymentRefund.getOrderNo());
                }
            } else {
                // 退款失败：设置支付订单状态为"退款失败(7)"
                paymentRefund.setRefundStatus(6);
                
                // 更新支付订单状态为退款失败
                PaymentOrder order = paymentOrderService.getByOrderNo(paymentRefund.getOrderNo());
                if (order != null) {
                    order.setOrderStatus(7L); // 退款失败
                    order.setUpdateBy(SecurityUtils.getUsername()); // 设置更新者为当前登录人
                    order.setUpdateTime(new Date());
                    paymentOrderService.updateById(order);
                    log.info("退款失败，订单状态已更新为退款失败：订单号={}", paymentRefund.getOrderNo());
                }
            }

            paymentRefund.setUpdateTime(new Date());
            this.updateById(paymentRefund);

            log.info("退款处理完成：退款申请号={}，退款结果={}", refundNo, refundSuccess);

        } catch (Exception e) {
            // 退款失败：设置支付订单状态为"退款失败(7)"
            paymentRefund.setRefundStatus(6);
            paymentRefund.setUpdateTime(new Date());
            this.updateById(paymentRefund);
            
            // 更新支付订单状态为退款失败
            PaymentOrder order = paymentOrderService.getByOrderNo(paymentRefund.getOrderNo());
            if (order != null) {
                order.setOrderStatus(7L); // 退款失败
                order.setUpdateBy(SecurityUtils.getUsername()); // 设置更新者为当前登录人
                order.setUpdateTime(new Date());
                paymentOrderService.updateById(order);
                log.info("退款处理异常，订单状态已更新为退款失败：订单号={}", paymentRefund.getOrderNo());
            }
            
            log.error("退款处理失败：{}", e.getMessage());
            throw new BaseException(e.getMessage());
        }
    }

    /**
     * 根据渠道执行退款
     */
    private boolean channelRefund(PaymentRefund paymentRefund) throws AlipayApiException {
        PaymentOrder order = paymentOrderService.getByOrderNo(paymentRefund.getOrderNo());
        if (order == null) {
            throw new RuntimeException("原订单不存在，无法退款");
        }
        String channel = paymentRefund.getRefundChannel();
        if (channel == null) {
            channel = order.getPayChannel();
        }
        if ("alipay".equalsIgnoreCase(channel)) {
            return doAlipayRefund(order, paymentRefund);
        }
        // 其他渠道可在此扩展
        throw new RuntimeException("暂不支持的退款渠道: " + channel);
    }

    private boolean doAlipayRefund(PaymentOrder order, PaymentRefund paymentRefund) throws AlipayApiException {
        DefaultAlipayClient alipayClient = new DefaultAlipayClient(
                GATEWAY_URL,
                aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(),
                "JSON",
                "UTF-8",
                aliPayConfig.getAlipayPublicKey(),
                "RSA2");

        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        String bizContent = "{" +
                "\"out_trade_no\":\"" + order.getOrderNo() + "\"," +
                // 可选：如果存有 trade_no 也可上传 "trade_no"
                "\"refund_amount\":" + paymentRefund.getRefundAmount() + "," +
                "\"refund_reason\":\"" + (paymentRefund.getRefundReason() == null ? "退款" : paymentRefund.getRefundReason()) + "\"" +
                "}";
        request.setBizContent(bizContent);
        AlipayTradeRefundResponse response = alipayClient.execute(request);
        boolean success = response.isSuccess();
        if (success) {
            paymentRefund.setThirdRefundNo(response.getTradeNo());
        }
        return success;
    }

    /**
     * 根据订单号查询退款申请
     *
     * @param orderNo 订单号
     * @return 退款申请列表
     */
    @Override
    public List<PaymentRefund> getRefundsByOrderNo(String orderNo) {
        return this.list(
                new LambdaQueryWrapper<PaymentRefund>()
                        .eq(PaymentRefund::getOrderNo, orderNo)
                        .orderByDesc(PaymentRefund::getCreateTime)
        );
    }

    /**
     * 检查订单是否可以申请退款
     * @param orderNo 订单号
     * @param userId 用户ID
     * @return 是否可以退款
     */
    @Override
    public boolean canApplyRefund(String orderNo, Long userId ) {
        // 查询原订单
        PaymentOrder order = paymentOrderService.getByOrderNo(orderNo);
        if (order == null) {
            return false;
        }

        // 检查订单是否属于该用户
        if (!order.getUserId().equals(userId)) {
            throw new BaseException("支付类型不能为空");
        }
        // 检查订单状态：按照字典定义，仅“已支付(2)/已完成(3)”可申请退款
        if (order.getOrderStatus() != 2 && order.getOrderStatus() != 3) {
            return false;
        }

        // 检查支付时间：超过30天不能申请退款（可根据业务需求调整）
        if (order.getPayTime() != null) {
            long daysSincePayment = (System.currentTimeMillis() - order.getPayTime().getTime()) / (1000 * 60 * 60 * 24);
            if (daysSincePayment > 30) {
                return false;
            }
        }

        // 不允许存在待处理退款（待审核(1)、退款中(4)）
        long pending = this.count(new LambdaQueryWrapper<PaymentRefund>()
                .eq(PaymentRefund::getOrderNo, orderNo)
                .eq(PaymentRefund::getUserId, userId)
                .in(PaymentRefund::getRefundStatus, 1, 4));
        if (pending > 0) {
            return false;
        }

        return true;
    }
}